#include <tsin/util/MemCheck.hpp>

#include <cstdio>
#include <cstdlib>
#include <cstddef>
#include <cassert>

using std::size_t;

#undef new

UTIL_DLLEXPORT bool traceFlag = false;
UTIL_DLLEXPORT bool activeFlag = false;

namespace tsin {
        namespace memCheck {
                struct Info {
                        void *ptr;
                        const char *file;
                        long line;
                };

                const size_t MAXPTRS = 10000u;
                Info memMap[MAXPTRS];
                size_t nptrs = 0;

                UTIL_DLLEXPORT int findPtr(void *p) {
                        for (size_t i = 0; i < nptrs; i++)
                                if (memMap[i].ptr == p)
                                        return i;
                        return -1;
                }

                UTIL_DLLEXPORT void delPtr(void *p) {
                        int pos = findPtr(p);
                        assert (p >= 0);
                        for (size_t i = pos; i < nptrs - 1; i ++)
                                memMap[i] = memMap[i + 1];
                        --nptrs;
                }

                struct Sentinel {
                        ~Sentinel () {
                                if (nptrs > 0) {
                                        printf("Leaked memory at:\n");
                                        for (size_t i = 0; i < nptrs; i ++)
                                                printf("\t%p (file: %s, line: %ld)\n",
                                                       memMap[i].ptr, memMap[i].file, memMap[i].line);
                                } else {
                                        printf("No user memory leaks.\n");
                                }
                        }
                };

                Sentinel s;
        }
}

using namespace tsin::memCheck;

UTIL_DLLEXPORT void *operator new(size_t siz, const char *file, long line)
{
        void *p = malloc(siz);
        if (activeFlag) {
                if (nptrs == MAXPTRS) {
                        printf("memory map too small\n");
                        exit(1);
                }
                memMap[nptrs].ptr = p;
                memMap[nptrs].file = file;
                memMap[nptrs].line = line;
                nptrs ++;
        }
        if (traceFlag) {
                printf("Allocated %u bytes at address %p ", siz, p);
                printf("(file: %s, line: %ld)\n", file, line);
        }
        return p;
}

UTIL_DLLEXPORT void *operator new[](size_t siz, const char *file, long line)
{
        return operator new(siz, file, line);
}